package demo.java.lang.management.jvm;

import demo.java.lang.management.vo.JVMThreadInfo;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.PrintStream;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.ArrayList;
import java.util.List;


/**
 * 收集线程相关的信息，如线程堆栈等
 */
public class ThreadingCollector {

    private static final Logger logger = LoggerFactory.getLogger(ThreadingCollector.class);

    private final ThreadMXBean Threading;

    public ThreadingCollector(ThreadMXBean Threading) {
        this.Threading = Threading;
    }

    public List<JVMThreadInfo> collect() {
        List<JVMThreadInfo> threadInfos = new ArrayList<JVMThreadInfo>();
        try {
            ThreadInfo[] threads = Threading.dumpAllThreads(false, false);
            for (ThreadInfo thread : threads) {
                if (thread.getThreadState() != null
                        && StringUtils.equalsIgnoreCase(thread.getThreadState().name(), "BLOCKED")) {
                    JVMThreadInfo threadInfo = new JVMThreadInfo();
                    threadInfo.setThreadID(thread.getThreadId());
                    threadInfo.setThreadName(thread.getThreadName());
                    threadInfo.setThreadState(thread.getThreadState().name());
                    threadInfo.setBlockedCount(thread.getBlockedCount());// jvm启动以来线程阻塞总次数
                    threadInfo.setBlockedTime(thread.getBlockedTime());// jvm启动以来线程阻塞的总时间
                    threadInfo.setWaitedCount(thread.getWaitedCount());// 线程在WAITING或TIMED_WAITING状态中的次数
                    threadInfo.setWaitedTime(thread.getWaitedTime());// WAITING or TIMED_WAITING state 时间
                    threadInfo.setInNative(thread.isInNative());// 与线程相关的native方法是否通过了测试
                    threadInfo.setSuspended(thread.isSuspended());// 与线程相关的线程是否挂起了
                    StackTraceElement[] stackTraceElements = thread.getStackTrace();
                    threadInfo.setStackTrace(stackToString(stackTraceElements));
                    threadInfos.add(threadInfo);
                }
            }
        } catch (Throwable e) {
            logger.error("ThreadingCollector collect error, {}", e.getMessage());
            return null;
        }

        return threadInfos;
    }

    private String stackToString(StackTraceElement[] stackTraceElements) {
        StringBuffer s = new StringBuffer();
        if (stackTraceElements != null && stackTraceElements.length > 0) {
            for (StackTraceElement element : stackTraceElements) {
                s.append(element.toString()).append("\r\n");
            }
        }
        return s.toString();
    }

    public int getDaemonThreadCount() {
        return Threading.getDaemonThreadCount();
    }

    public int getThreadCount() {
        return Threading.getThreadCount();
    }

    public int getPeakThreadCount() {
        return Threading.getPeakThreadCount();
    }

    public void resetPeakThreadCount() {
        Threading.resetPeakThreadCount();
    }

    public long getTotalStartedThreadCount() {
        return Threading.getTotalStartedThreadCount();
    }

    public int getRunnableThreadCount() {
        return getThreadCountByState("RUNNABLE");
    }

    public int getNewThreadCount() {
        return getThreadCountByState("NEW");
    }

    public int getBlockedThreadCount() {
        return getThreadCountByState("BLOCKED");
    }

    public int getWaitingThreadCount() {
        return getThreadCountByState("WAITING");
    }

    public int getTimedWaitingThreadCount() {
        return getThreadCountByState("TIMED_WAITING");
    }

    public int getTeminatedThreadCount() {
        return getThreadCountByState("TERMINATED");
    }

    public int getDeadLockedThreadCount() {
        try {
            long[] deadLockedThreadIds = Threading.findDeadlockedThreads();

            if (deadLockedThreadIds == null) {
                return 0;
            }

            return deadLockedThreadIds.length;
        } catch (Exception e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
    }

    private int getThreadCountByState(final String state) {
        ThreadInfo[] threadInfoArray = dumpAllThreads();
        int runnableCount = 0;
        for (ThreadInfo threadInfo : threadInfoArray) {
            if (state.equals(threadInfo.getThreadState().name())) {
                runnableCount++;
            }
        }

        return runnableCount;
    }

    private ThreadInfo[] dumpAllThreads() {
        return Threading.dumpAllThreads(false, false);
    }

    public void dump(PrintStream out) {
        out.println("DaemonThreadCount : " + this.getDaemonThreadCount());
        out.println("ThreadCount : " + this.getThreadCount());

        out.println("NewThreadCount : " + this.getNewThreadCount());
        out.println("RunnableThreadCount : " + this.getRunnableThreadCount());
        out.println("BlockedThreadCount : " + this.getBlockedThreadCount());
        out.println("WaitingThreadCount : " + this.getWaitingThreadCount());
        out.println("TimedWaitingThreadCount : " + this.getTimedWaitingThreadCount());
        out.println("TeminatedThreadCount : " + this.getTeminatedThreadCount());

        out.println("DeadLockedThreadCount : " + this.getDeadLockedThreadCount());
        out.println("TotalStartedThreadCount : " + this.getTotalStartedThreadCount());

    }

    public void dump() {
        this.getDaemonThreadCount();
        this.getThreadCount();

        this.getNewThreadCount();
        this.getRunnableThreadCount();
        this.getBlockedThreadCount();
        this.getWaitingThreadCount();
        this.getTimedWaitingThreadCount();
        this.getTeminatedThreadCount();

        this.getDeadLockedThreadCount();
        this.getTotalStartedThreadCount();

    }
}
